package com.stu.chart.util;


import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;

public class CalXieUtil {

    /**
     * 推荐人
     * 计算 master 用户之间的 关系度   获取最高的前 top 位
     * @param userPerfMap
     * @param master
     * @return 返回  皮尔逊相关系数
     */
    public static Map<String, Double> getSimUserSim(Map<String, Map<String, Integer>> userPerfMap, String master,Integer top) {

        Map<String, Integer>  masterMap = userPerfMap.get(master);
        Map<String, Double> simUserSimMap = new HashMap<String, Double>();
        System.out.println("皮尔逊相关系数:");

        for (Entry<String, Map<String, Integer>> userPerfEn : userPerfMap.entrySet()) {
            String userName = userPerfEn.getKey();
            if (!master.equals(userName)) {
                double sim = getUserSimilar(masterMap, userPerfEn.getValue());
                System.out.println(  master + "与" + userName + "之间的相关系数:" + sim);
                simUserSimMap.put(userName, sim);
            }
        }
        HashMap<String,Double> result = new LinkedHashMap<>();
        List<Entry<String, Double>> collect = simUserSimMap.entrySet().stream().sorted((p1, p2) -> p2.getValue().compareTo(p1.getValue()))
                .collect(Collectors.toList());
        for (int i = 0; i < collect.size(); i++) {
            if(top !=null && i==top){
                break;
            }
            Entry<String, Double> stringDoubleEntry = collect.get(i);
            result.put(stringDoubleEntry.getKey(),stringDoubleEntry.getValue());
        }
        return  result;
    }


    //Claculate Pearson Correlation Coefficient
    public static double getUserSimilar(Map<String, Integer> pm1, Map<String, Integer> pm2) {
        int n = 0;// 数量n
        int sxy = 0;// Σxy=x1*y1+x2*y2+....xn*yn
        int sx = 0;// Σx=x1+x2+....xn
        int sy = 0;// Σy=y1+y2+...yn
        int sx2 = 0;// Σx2=(x1)2+(x2)2+....(xn)2
        int sy2 = 0;// Σy2=(y1)2+(y2)2+....(yn)2
        for (Entry<String, Integer> pme : pm1.entrySet()) {
            String key = pme.getKey();
            Integer x = pme.getValue();
            Integer y = pm2.get(key);
            if (x != null && y != null) {
                n++;
                sxy += x * y;
                sx += x;
                sy += y;
                sx2 += Math.pow(x, 2);
                sy2 += Math.pow(y, 2);
            }
        }
        if(n == 0){
            return 1;
        }
        // p=(Σxy-Σx*Σy/n)/Math.sqrt((Σx2-(Σx)2/n)(Σy2-(Σy)2/n));
        double sd = sxy - sx * sy / n;
        double sm = Math.sqrt((sx2 - Math.pow(sx, 2) / n) * (sy2 - Math.pow(sy, 2) / n));
        return Math.abs(sm == 0 ? 1 : sd / sm);
    }

    /**
     * 获取推荐结果 推荐物
     * @param simUserObjMap    用户  -->  <物，次数>
     * @param simUserSimMap    用户
     * @return
     */
    public static List<String> getRecommend(Map<String, Map<String, Integer>> simUserObjMap,
                                      Map<String, Double> simUserSimMap,Integer top) {
        Map<String, Double> objScoreMap = new HashMap<String, Double>();
        for (Entry<String, Map<String, Integer>> simUserEn : simUserObjMap.entrySet()) {
            String user = simUserEn.getKey();
            double sim = simUserSimMap.get(user);
            for (Entry<String, Integer> simObjEn : simUserEn.getValue().entrySet()) {
                double objScore = sim * simObjEn.getValue();//加权（相似度*评分）
                String objName = simObjEn.getKey();
                if (objScoreMap.get(objName) == null) {
                    objScoreMap.put(objName, objScore);
                } else {
                    double totalScore = objScoreMap.get(objName);
                    objScoreMap.put(objName, totalScore + objScore);//将所有用户的加权评分作为最后的推荐结果数据
                }
            }
        }
        List<Entry<String, Double>> enList = new ArrayList<Entry<String, Double>>(objScoreMap.entrySet());
        Collections.sort(enList, new Comparator<Entry<String, Double>>() {//排序
            public int compare(Entry<String, Double> o1, Entry<String, Double> o2) {
                Double a = o1.getValue() - o2.getValue();
                if (a == 0) {
                    return 0;
                } else if (a > 0) {
                    return 1;
                } else {
                    return -1;
                }
            }
        });
        for (Entry<String, Double> entry : enList) {
            System.out.println(entry.getKey()+"的加权推荐值:"+entry.getValue());
        }
        List<String> result = new ArrayList<>();
        for (int i = 0; i < enList.size(); i++) {
            if(i == top){
                break;
            }
            result.add(enList.get(i).getKey());
        }
        return result;//返回推荐结果
    }
}
